package org.richin.lang.util;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import org.richin.collection.util.ListUtils;
import org.richin.reflection.util.ReflectUtils;

public class Convert {
	public static Convert instance = new Convert();

	public static Convert getInstance() {
		return instance;
	}

	public static void setInstance(Convert value) {
		instance = value;
	};

	public Object booleanToObject(boolean value) {
		return value ? Boolean.TRUE : Boolean.FALSE;
	}

	public boolean objectToBoolean(Object obj) {
		return !(obj instanceof Boolean) || ((Boolean) obj).booleanValue();
	}

	public static Object toObject(boolean value) {
		return instance.booleanToObject(value);
	}

	public static boolean toBoolean(Object obj) {
		return instance.objectToBoolean(obj);
	}

	public Object charToObject(char ch) {
		return new Character(ch);
	}

	public char objectToChar(Object obj) {
		return ((Character) obj).charValue();
	}

	public static Object toObject(char ch) {
		return instance.charToObject(ch);
	}

	public static char toChar(Object obj) {
		return instance.objectToChar(obj);
	}

	public Object byteToObject(byte value) {
		return new Byte(value);
	}

	public byte objectToByte(Object obj) {
		return ((Number) obj).byteValue();
	}

	public static Object toObject(byte value) {
		return instance.byteToObject(value);
	}

	public static byte toByte(Object obj) {
		return instance.objectToByte(obj);
	}

	public Object byteToObjectUnsigned(byte value) {
		return new Integer(value & 0xFF);
	}

	public byte objectToByteUnsigned(Object obj) {
		return ((Number) obj).byteValue();
	}

	public static Object toObjectUnsigned(byte value) {
		return instance.byteToObjectUnsigned(value);
	}

	public static byte toByteUnsigned(Object obj) {
		return instance.objectToByteUnsigned(obj);
	}

	public Object shortToObject(short value) {
		return new Short(value);
	}

	public short objectToShort(Object obj) {
		return ((Number) obj).shortValue();
	}

	public static Object toObject(short value) {
		return instance.shortToObject(value);
	}

	public static short toShort(Object obj) {
		return instance.objectToShort(obj);
	}

	public Object shortToObjectUnsigned(short value) {
		return new Integer(value & 0xFFFF);
	}

	public short objectToShortUnsigned(Object obj) {
		return ((Number) obj).shortValue();
	}

	public static Object toObjectUnsigned(short value) {
		return instance.shortToObjectUnsigned(value);
	}

	public static short toShortUnsigned(Object obj) {
		return instance.objectToShortUnsigned(obj);
	}

	public Object intToObject(int value) {
		return new Integer(value);
	}

	public int objectToInt(Object obj) {
		if (obj instanceof String)
			return NumberUtils.toInt((String) obj);
		return NumberUtils.convertNumberToTargetClass(((Number) obj), Integer.class).intValue();
	}

	public static Object toObject(int value) {
		return instance.intToObject(value);
	}

	public static int toInt(Object obj) {

		return instance.objectToInt(obj);
	}

	public Object intToObjectUnsigned(int value) {
		if (value >= 0)
			return new Integer(value);
		else
			return new Long((long) value & 0xffffffffL);
	}

	public int objectToIntUnsigned(Object obj) {
		return ((Number) obj).intValue();
	}

	public static Object toObjectUnsigned(int value) {
		return instance.intToObjectUnsigned(value);
	}

	public static int toIntUnsigned(Object obj) {
		return instance.objectToIntUnsigned(obj);
	}

	public Object longToObject(long value) {
		return new Long(value);
	}

	public long objectToLong(Object obj) {
		if (obj instanceof String)
			return NumberUtils.toLong((String) obj);
		return NumberUtils.convertNumberToTargetClass(((Number) obj), Long.class).longValue();
	}

	public static Object toObject(long value) {
		return instance.longToObject(value);
	}

	public static long toLong(Object obj) {
		return instance.objectToLong(obj);
	}

	public Object longToObjectUnsigned(long value) {
		return new Long(value); // FIXME use BigInteger?
	}

	public long objectToLongUnsigned(Object obj) {
		return ((Number) obj).longValue();
	}

	public static Object toObjectUnsigned(long value) {
		return instance.longToObjectUnsigned(value);
	}

	public static long toLongUnsigned(Object obj) {
		return instance.objectToLongUnsigned(obj);
	}

	public Object floatToObject(float value) {
		return new Float(value);
	}

	public float objectToFloat(Object obj) {
		return ((Number) obj).floatValue();
	}

	public static Object toObject(float value) {
		return instance.floatToObject(value);
	}

	public static float toFloat(Object obj) {
		return instance.objectToFloat(obj);
	}

	public Object doubleToObject(double value) {
		return new Double(value);
	}

	public double objectToDouble(Object obj) {
		return ((Number) obj).doubleValue();
	}

	public static Object toObject(double value) {
		return instance.doubleToObject(value);
	}

	public static double toDouble(Object obj) {
		return instance.objectToDouble(obj);
	}

	public static double parseDouble(String str) {
		// We assume that if collections are available the Double.parseDouble
		// is also available.
		/* #ifdef JAVA2 */
		return Double.parseDouble(str);
		/* #endif */
		/* #ifndef JAVA2 */
		// return Double.valueOf(str).doubleValue();
		/* #endif */
	}

	public static Number castToNumber(Object object) {
		// default to Number class in exception details, else use the specified Number subtype.
		return castToNumber(object, Number.class);
	}

	public static Number castToNumber(Object object, Class type) {
		if (object instanceof Number)
			return (Number) object;
		if (object instanceof Character) {
			return Integer.valueOf(((Character) object).charValue());
		}
		if (object instanceof String) {
			String c = (String) object;
			if (c.length() == 1) {
				return Integer.valueOf(c.charAt(0));
			} else {
				convertException(c, type);
			}
		}
		convertException(object, type);
		return null;
	}

	private static void convertException(Object number, Class targetClass) {
		throw new IllegalArgumentException("Could not convert number [" + number + "] of type ["
				+ number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow");
	}
	 public static Object castToType(Object object, Class type) throws IllegalAccessException {
	        if (object == null) {
	            return null;
	        }

	        if (type == Object.class)
	          return object;

	        final Class aClass = object.getClass();
	        if (type == aClass) return object;
	        // TODO we should move these methods to groovy method, like g$asType() so that
	        // we can use operator overloading to customize on a per-type basis
	        if (ReflectUtils.isArray(type)) {
	            return asArray(object, type);

	        }
	        if (ReflectUtils.isAssignableFrom(type, aClass)) {
	            return object;
	        }
	        if (Collection.class.isAssignableFrom(type)) {
	            int modifiers = type.getModifiers();
	            Collection answer;
	            if (object instanceof Collection && type.isAssignableFrom(HashSet.class) &&
	                    (type == HashSet.class || Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
	                return new HashSet((Collection)object);
	            }
	            if (aClass.isArray()) {
	                if (type.isAssignableFrom(ArrayList.class) && (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
	                    answer = new ArrayList();
	                } else {
	                    // let's call the collections constructor
	                    // passing in the list wrapper
	                    try {
	                        answer = (Collection) type.newInstance();
	                    }
	                    catch (Exception e) {
	                        throw new IllegalAccessException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
	                    }
	                }

	                // we cannot just wrap in a List as we support primitive type arrays
	                int length = Array.getLength(object);
	                for (int i = 0; i < length; i++) {
	                    Object element = Array.get(object, i);
	                    answer.add(element);
	                }
	                return answer;
	            }
	        }
	        if (type == String.class) {
	            return object.toString();
	        } else if (type == Character.class) {
	            return box(castToChar(object));
	        } else if (type == Boolean.class) {
	            return box(castToBoolean(object));
	        } else if (type == Class.class) {
	            return castToClass(object);
	        } else if (Number.class.isAssignableFrom(type)) {
	            Number n = castToNumber(object, type);
	            if (type == Byte.class) {
	                return new Byte(n.byteValue());
	            } else if (type == Character.class) {
	                return new Character((char) n.intValue());
	            } else if (type == Short.class) {
	                return new Short(n.shortValue());
	            } else if (type == Integer.class) {
	                return Integer.valueOf(n.intValue());
	            } else if (type == Long.class) {
	                return new Long(n.longValue());
	            } else if (type == Float.class) {
	                return new Float(n.floatValue());
	            } else if (type == Double.class) {
	                Double answer = new Double(n.doubleValue());
	                //throw a runtime exception if conversion would be out-of-range for the type.
	                if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
	                        || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
	                    throw new IllegalAccessException("Automatic coercion of " + n.getClass().getName()
	                            + " value " + n + " to double failed.  Value is out of range.");
	                }
	                return answer;
	            } else if (type == BigDecimal.class) {
	                return new BigDecimal(n.toString());
	            } else if (type == BigInteger.class) {
	                if (object instanceof Float || object instanceof Double) {
	                    BigDecimal bd = new BigDecimal(n.doubleValue());
	                    return bd.toBigInteger();
	                } else if (object instanceof BigDecimal) {
	                    return ((BigDecimal) object).toBigInteger();
	                } else {
	                    return new BigInteger(n.toString());
	                }
	            }
	        } else if (type.isPrimitive()) {
	            if (type == boolean.class) {
	               return box(booleanUnbox(object));
	            } else if (type == byte.class) {
	                return box(byteUnbox(object));
	            } else if (type == char.class) {
	                return box(charUnbox(object));
	            } else if (type == short.class) {
	                return box(shortUnbox(object));
	            } else if (type == int.class) {
	                return box(intUnbox(object));
	            } else if (type == long.class) {
	                return box(longUnbox(object));
	            } else if (type == float.class) {
	                return box(floatUnbox(object));
	            } else if (type == double.class) {
	                Double answer = new Double(doubleUnbox(object));
	                //throw a runtime exception if conversion would be out-of-range for the type.
	                if (!(object instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
	                        || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
	                    throw new IllegalAccessException("Automatic coercion of " + aClass.getName()
	                            + " value " + object + " to double failed.  Value is out of range.");
	                }
	                return answer;
	            }
	        }
	        Object[] args = null;
	        if (object instanceof Collection) {
	            // let's try invoke the constructor with the list as arguments
	            // such as for creating a Dimension, Point, Color etc.
	            Collection collection = (Collection) object;
	            args = collection.toArray();
	        } else if (object instanceof Object[]) {
	            args = (Object[]) object;
	        } else if (object instanceof Map) {
	            // emulate named params constructor
	            args = new Object[1];
	            args[0] = object;
	        }
	        throw new IllegalAccessException(""+object);
	    }
	 public static Object asArray(Object object, Class type) throws IllegalAccessException {
	        if (type.isAssignableFrom(object.getClass())) {
	            return object;
	        }
	        Collection list = asCollection(object);
	        int size = list.size();
	        Class elementType = type.getComponentType();
	        Object array = Array.newInstance(elementType, size);
	        int idx = 0;

	        if (boolean.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setBoolean(array, idx, booleanUnbox(element));
	            }
	        }
	        else if (byte.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setByte(array, idx, byteUnbox(element));
	            }
	        }
	        else if (char.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setChar(array, idx, charUnbox(element));
	            }
	        }
	        else if (double.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setDouble(array, idx, doubleUnbox(element));
	            }
	        }
	        else if (float.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setFloat(array, idx, floatUnbox(element));
	            }
	        }
	        else if (int.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setInt(array, idx, intUnbox(element));
	            }
	        }
	        else if (long.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setLong(array, idx, longUnbox(element));
	            }
	        }
	        else if (short.class.equals(elementType)) {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Array.setShort(array, idx, shortUnbox(element));
	            }
	        }
	        else {
	            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
	                Object element = iter.next();
	                Object coercedElement = castToType(element, elementType);
	                Array.set(array, idx, coercedElement);
	            }
	        }
	        return array;
	    }
	 public static Collection asCollection(Object value) {
	        if (value == null) {
	            return Collections.EMPTY_LIST;
	        }
	        else if (value instanceof Collection) {
	            return (Collection) value;
	        }
	        else if (value instanceof Map) {
	            Map map = (Map) value;
	            return map.entrySet();
	        }
	        else if (value.getClass().isArray()) {
	            return arrayAsCollection(value);
	        }
	        else if (value instanceof String) {
	            return ListUtils.toList((String) value);
	        }
	        else {
	            // let's assume it's a collection of 1
	            return Collections.singletonList(value);
	        }
	    }

	    public static Collection arrayAsCollection(Object value) {
	        if (value.getClass().getComponentType().isPrimitive()) {
	            return primitiveArrayToList(value);
	        }
	        return Arrays.asList((Object[]) value);
	    }
	//  --------------------------------------------------------
	    //                  unboxing methods
	    //  --------------------------------------------------------

	    public static byte byteUnbox(Object value) {
	        Number n = castToNumber(value);
	        return n.byteValue();
	    }

	    public static char charUnbox(Object value) throws IllegalAccessException {
	        return castToChar(value);
	    }

	    public static short shortUnbox(Object value) {
	        Number n = castToNumber(value);
	        return n.shortValue();
	    }

	    public static int intUnbox(Object value) {
	        Number n = castToNumber(value);
	        return n.intValue();
	    }

	    public static boolean booleanUnbox(Object value) {
	        return castToBoolean(value);
	    }

	    public static long longUnbox(Object value) {
	        Number n = castToNumber(value);
	        return n.longValue();
	    }

	    public static float floatUnbox(Object value) {
	        Number n = castToNumber(value);
	        return n.floatValue();
	    }

	    public static double doubleUnbox(Object value) {
	        Number n = castToNumber(value);
	        return n.doubleValue();
	    }

	    //  --------------------------------------------------------
	    //                  boxing methods
	    //  --------------------------------------------------------

	    public static Object box(boolean value) {
	        return value ? Boolean.TRUE : Boolean.FALSE;
	    }

	    public static Object box(byte value) {
	        return Byte.valueOf(value);
	    }

	    public static Object box(char value) {
	        return Character.valueOf(value);
	    }

	    public static Object box(short value) {
	        return Short.valueOf(value);
	    }

	    public static Object box(int value) {
	        return Integer.valueOf(value);
	    }

	    public static Object box(long value) {
	        return Long.valueOf(value);
	    }

	    public static Object box(float value) {
	        return Float.valueOf(value);
	    }

	    public static Object box(double value) {
	        return Double.valueOf(value);
	    }

	    /**
	     * Determines whether the value object is a Class object representing a
	     * subclass of java.lang.Enum. Uses class name check to avoid breaking on
	     * pre-Java 5 JREs.
	     *
	     * @param value an object
	     * @return true if the object is an Enum
	     */
	    public static boolean isEnumSubclass(Object value) {
	        if (value instanceof Class) {
	            Class superclass = ((Class)value).getSuperclass();
	            while (superclass != null) {
	                if (superclass.getName().equals("java.lang.Enum")) {
	                    return true;
	                }
	                superclass = superclass.getSuperclass();
	            }
	        }

	        return false;
	    }

	    /**
	     * Allows conversion of arrays into a mutable List
	     *
	     * @param array an array
	     * @return the array as a List
	     */
	    public static List primitiveArrayToList(Object array) {
	        int size = Array.getLength(array);
	        List list = new ArrayList(size);
	        for (int i = 0; i < size; i++) {
	            Object item = Array.get(array, i);
	            if (item != null && item.getClass().isArray() && item.getClass().getComponentType().isPrimitive()) {
	                item = primitiveArrayToList(item);
	            }
	            list.add(item);
	        }
	        return list;
	    }

	    public static Object[] primitiveArrayBox(Object array) {
	        int size = Array.getLength(array);
	        Object[] ret = (Object[]) Array.newInstance(ReflectUtils.autoboxType(array.getClass().getComponentType()), size);
	        for (int i = 0; i < size; i++) {
	            ret[i]=Array.get(array, i);
	        }
	        return ret;
	    }
	    public static boolean castToBoolean(Object object) {
	        if (object == null) {
	                return false;
	        }
	        if (object instanceof Boolean) {
	            Boolean booleanValue = (Boolean) object;
	            return booleanValue.booleanValue();
	        }
	        if (object instanceof Matcher) {
	            Matcher matcher = (Matcher) object;
	           
	            return matcher.find();
	        }
	        if (object instanceof Collection) {
	            Collection collection = (Collection) object;
	            return !collection.isEmpty();
	        }
	        if (object instanceof Map) {
	            Map map = (Map) object;
	            return !map.isEmpty();
	        }
	        if (object instanceof Iterator) {
	            Iterator iterator = (Iterator) object;
	            return iterator.hasNext();
	        }
	        if (object instanceof Enumeration) {
	            Enumeration e = (Enumeration) object;
	            return e.hasMoreElements();
	        }
	        if (object instanceof CharSequence) {
	                CharSequence string =  (CharSequence) object;
	            return string.length() > 0;
	        }
	        if (object instanceof Object[]) {
	                Object[] array =  (Object[]) object;
	            return array.length > 0;
	        }
	        if (object instanceof Character) {
	            Character c = (Character) object;
	            return c.charValue() != 0;
	        }
	        if (object instanceof Number) {
	            Number n = (Number) object;
	            return n.doubleValue() != 0;
	        }
	        return true;
	    }

	    public static char castToChar(Object object) throws IllegalAccessException {
	        if (object instanceof Character) {
	            return ((Character) object).charValue();
	        } else if (object instanceof Number) {
	            Number value = (Number) object;
	            return (char) value.intValue();
	        } else {
	            String text = object.toString();
	            if (text.length() == 1) {
	                return text.charAt(0);
	            }
	            else {
	                throw new IllegalAccessException(text);
	            }
	        }
	    }
	    private static Class castToClass(Object object) throws IllegalAccessException {
	        try {
	            return Class.forName (object.toString());
	        } catch (Exception e) {
	            throw new IllegalAccessException(""+object);
	        }
	    }
	public static void main(String[] args) {
		Object length="19888174";
		
		System.out.println(toLong(length));
	}
}
